// This code is derived from jcifs smb client library <jcifs at samba dot org>
// Ported by J. Arturo <webmaster at komodosoft dot net>
//  
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
using System.Text;
using SharpCifs.Util;

namespace SharpCifs.Smb
{
    /// <summary>
    /// An Access Control Entry (ACE) is an element in a security descriptor
    /// such as those associated with files and directories.
    /// </summary>
    /// <remarks>
    /// An Access Control Entry (ACE) is an element in a security descriptor
    /// such as those associated with files and directories. The Windows OS
    /// determines which users have the necessary permissions to access objects
    /// based on these entries.
    /// <p>
    /// To fully understand the information exposed by this class a description
    /// of the access check algorithm used by Windows is required. The following
    /// is a basic description of the algorithm. For a more complete description
    /// we recommend reading the section on Access Control in Keith Brown's
    /// "The .NET Developer's Guide to Windows Security" (which is also
    /// available online).
    /// <p>
    /// Direct ACEs are evaluated first in order. The SID of the user performing
    /// the operation and the desired access bits are compared to the SID
    /// and access mask of each ACE. If the SID matches, the allow/deny flags
    /// and access mask are considered. If the ACE is a "deny"
    /// ACE and <i>any</i> of the desired access bits match bits in the access
    /// mask of the ACE, the whole access check fails. If the ACE is an "allow"
    /// ACE and <i>all</i> of the bits in the desired access bits match bits in
    /// the access mask of the ACE, the access check is successful. Otherwise,
    /// more ACEs are evaluated until all desired access bits (combined)
    /// are "allowed". If all of the desired access bits are not "allowed"
    /// the then same process is repeated for inherited ACEs.
    /// <p>
    /// For example, if user <tt>WNET\alice</tt> tries to open a file
    /// with desired access bits <tt>0x00000003</tt> (<tt>FILE_READ_DATA |
    /// FILE_WRITE_DATA</tt>) and the target file has the following security
    /// descriptor ACEs:
    /// <pre>
    /// Allow WNET\alice     0x001200A9  Direct
    /// Allow Administrators 0x001F01FF  Inherited
    /// Allow SYSTEM         0x001F01FF  Inherited
    /// </pre>
    /// the access check would fail because the direct ACE has an access mask
    /// of <tt>0x001200A9</tt> which doesn't have the
    /// <tt>FILE_WRITE_DATA</tt> bit on (bit <tt>0x00000002</tt>). Actually, this isn't quite correct. If
    /// <tt>WNET\alice</tt> is in the local <tt>Administrators</tt> group the access check
    /// will succeed because the inherited ACE allows local <tt>Administrators</tt>
    /// both <tt>FILE_READ_DATA</tt> and <tt>FILE_WRITE_DATA</tt> access.
    /// </remarks>
    public class Ace
    {
        public const int FileReadData = unchecked(0x00000001);

        public const int FileWriteData = unchecked(0x00000002);

        public const int FileAppendData = unchecked(0x00000004);

        public const int FileReadEa = unchecked(0x00000008);

        public const int FileWriteEa = unchecked(0x00000010);

        public const int FileExecute = unchecked(0x00000020);

        public const int FileDelete = unchecked(0x00000040);

        public const int FileReadAttributes = unchecked(0x00000080);

        public const int FileWriteAttributes = unchecked(0x00000100);

        public const int Delete = unchecked(0x00010000);

        public const int ReadControl = unchecked(0x00020000);

        public const int WriteDac = unchecked(0x00040000);

        public const int WriteOwner = unchecked(0x00080000);

        public const int Synchronize = unchecked(0x00100000);

        public const int GenericAll = unchecked(0x10000000);

        public const int GenericExecute = unchecked(0x20000000);

        public const int GenericWrite = unchecked(0x40000000);

        public const int GenericRead = unchecked((int)(0x80000000));

        public const int FlagsObjectInherit = unchecked(0x01);

        public const int FlagsContainerInherit = unchecked(0x02);

        public const int FlagsNoPropagate = unchecked(0x04);

        public const int FlagsInheritOnly = unchecked(0x08);

        public const int FlagsInherited = unchecked(0x10);

        internal bool Allow;

        internal int Flags;

        internal int Access;

        internal Sid Sid;

        // 1
        // 2
        // 3
        // 4
        // 5
        // 6
        // 7
        // 8
        // 9
        // 16
        // 17
        // 18
        // 19
        // 20
        // 28
        // 29
        // 30
        // 31
        /// <summary>Returns true if this ACE is an allow ACE and false if it is a deny ACE.</summary>
        /// <remarks>Returns true if this ACE is an allow ACE and false if it is a deny ACE.</remarks>
        public virtual bool IsAllow()
        {
            return Allow;
        }

        /// <summary>Returns true if this ACE is an inherited ACE and false if it is a direct ACE.
        /// </summary>
        /// <remarks>
        /// Returns true if this ACE is an inherited ACE and false if it is a direct ACE.
        /// <p>
        /// Note: For reasons not fully understood, <tt>FLAGS_INHERITED</tt> may
        /// not be set within all security descriptors even though the ACE was in
        /// face inherited. If an inherited ACE is added to a parent the Windows
        /// ACL editor will rebuild all children ACEs and set this flag accordingly.
        /// </remarks>
        public virtual bool IsInherited()
        {
            return (Flags & FlagsInherited) != 0;
        }

        /// <summary>Returns the flags for this ACE.</summary>
        /// <remarks>
        /// Returns the flags for this ACE. The <tt>isInherited()</tt>
        /// method checks the <tt>FLAGS_INHERITED</tt> bit in these flags.
        /// </remarks>
        public virtual int GetFlags()
        {
            return Flags;
        }

        /// <summary>
        /// Returns the 'Apply To' text for inheritance of ACEs on
        /// directories such as 'This folder, subfolder and files'.
        /// </summary>
        /// <remarks>
        /// Returns the 'Apply To' text for inheritance of ACEs on
        /// directories such as 'This folder, subfolder and files'. For
        /// files the text is always 'This object only'.
        /// </remarks>
        public virtual string GetApplyToText()
        {
            switch (Flags & (FlagsObjectInherit | FlagsContainerInherit | FlagsInheritOnly))
            {
                case unchecked(0x00):
                    {
                        return "This folder only";
                    }

                case unchecked(0x03):
                    {
                        return "This folder, subfolders and files";
                    }

                case unchecked(0x0B):
                    {
                        return "Subfolders and files only";
                    }

                case unchecked(0x02):
                    {
                        return "This folder and subfolders";
                    }

                case unchecked(0x0A):
                    {
                        return "Subfolders only";
                    }

                case unchecked(0x01):
                    {
                        return "This folder and files";
                    }

                case unchecked(0x09):
                    {
                        return "Files only";
                    }
            }
            return "Invalid";
        }

        /// <summary>Returns the access mask accociated with this ACE.</summary>
        /// <remarks>
        /// Returns the access mask accociated with this ACE. Use the
        /// constants for <tt>FILE_READ_DATA</tt>, <tt>FILE_WRITE_DATA</tt>,
        /// <tt>READ_CONTROL</tt>, <tt>GENERIC_ALL</tt>, etc with bitwise
        /// operators to determine which bits of the mask are on or off.
        /// </remarks>
        public virtual int GetAccessMask()
        {
            return Access;
        }

        /// <summary>Return the SID associated with this ACE.</summary>
        /// <remarks>Return the SID associated with this ACE.</remarks>
        public virtual Sid GetSid()
        {
            return Sid;
        }

        internal virtual int Decode(byte[] buf, int bi)
        {
            Allow = buf[bi++] == unchecked(unchecked(0x00));
            Flags = buf[bi++] & unchecked(0xFF);
            int size = ServerMessageBlock.ReadInt2(buf, bi);
            bi += 2;
            Access = ServerMessageBlock.ReadInt4(buf, bi);
            bi += 4;
            Sid = new Sid(buf, bi);
            return size;
        }

        internal virtual void AppendCol(StringBuilder sb, string str, int width)
        {
            sb.Append(str);
            int count = width - str.Length;
            for (int i = 0; i < count; i++)
            {
                sb.Append(' ');
            }
        }

        /// <summary>Return a string represeting this ACE.</summary>
        /// <remarks>
        /// Return a string represeting this ACE.
        /// <p>
        /// Note: This function should probably be changed to return SDDL
        /// fragments but currently it does not.
        /// </remarks>
        public override string ToString()
        {
            int count;
            int i;
            string str;
            StringBuilder sb = new StringBuilder();
            sb.Append(IsAllow()
                        ? "Allow "
                        : "Deny  ");
            AppendCol(sb, Sid.ToDisplayString(), 25);
            sb.Append(" 0x").Append(Hexdump.ToHexString(Access, 8)).Append(' ');
            sb.Append(IsInherited()
                        ? "Inherited "
                        : "Direct    ");
            AppendCol(sb, GetApplyToText(), 34);
            return sb.ToString();
        }
    }
}
